home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / Text / Edit / ElVIs / src / blk.c < prev    next >
C/C++ Source or Header  |  1992-04-07  |  9KB  |  470 lines

  1. /* blk.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains the functions that get/put blocks from the temp file.
  12.  * It also contains the "do" and "undo" functions.
  13.  */
  14.  
  15. #include "config.h"
  16. #include "vi.h"
  17.  
  18. #ifndef NBUFS
  19. # define NBUFS    5        /* must be at least 3 -- more is better */
  20. #endif
  21.  
  22.  
  23. /*------------------------------------------------------------------------*/
  24.  
  25. BLK        hdr;        /* buffer for the header block */
  26.  
  27. static int    b4cnt;        /* used to count context of beforedo/afterdo */
  28. static struct _blkbuf
  29. {
  30.     BLK        buf;        /* contents of a text block */
  31.     unsigned short    logical;    /* logical block number */
  32.     int        dirty;        /* must the buffer be rewritten? */
  33. }
  34.         blk[NBUFS],    /* buffers for text[?] blocks */
  35.         *toonew,    /* buffer which shouldn't be recycled yet */
  36.         *newtoo,    /* another buffer which should be recycled */
  37.         *recycle = blk;    /* next block to be recycled */
  38.  
  39.  
  40.  
  41.  
  42.  
  43. /* This function wipes out all buffers */
  44. void blkinit()
  45. {
  46.     int    i;
  47.  
  48.     for (i = 0; i < NBUFS; i++)
  49.     {
  50.         blk[i].logical = 0;
  51.         blk[i].dirty = FALSE;
  52.     }
  53.     for (i = 0; i < MAXBLKS; i++)
  54.     {
  55.         hdr.n[i] = 0;
  56.     }
  57. }
  58.  
  59. /* This function allocates a buffer and fills it with a given block's text */
  60. BLK *blkget(logical)
  61.     int    logical;    /* logical block number to fetch */
  62. {
  63.     REG struct _blkbuf    *this;    /* used to step through blk[] */
  64.     REG int    i;
  65.  
  66.     /* if logical is 0, just return the hdr buffer */
  67.     if (logical == 0)
  68.     {
  69.         return &hdr;
  70.     }
  71.  
  72.     /* see if we have that block in mem already */
  73.     for (this = blk; this < &blk[NBUFS]; this++)
  74.     {
  75.         if (this->logical == logical)
  76.         {
  77.             newtoo = toonew;
  78.             toonew = this;
  79.             return &this->buf;
  80.         }
  81.     }
  82.  
  83.     /* choose a block to be recycled */
  84.     do
  85.     {
  86.         this = recycle++;
  87.         if (recycle == &blk[NBUFS])
  88.         {
  89.             recycle = blk;
  90.         }
  91.     } while (this == toonew || this == newtoo);
  92.  
  93.     /* if it contains a block, flush that block */
  94.     blkflush(this);
  95.  
  96.     /* fill this buffer with the desired block */
  97.     this->logical = logical;
  98.     if (hdr.n[logical])
  99.     {
  100.         /* it has been used before - fill it from tmp file */
  101.         lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0);
  102.         if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
  103.         {
  104.             msg("Error reading back from tmp file!");
  105.         }
  106.     }
  107.     else
  108.     {
  109.         /* it is new - zero it */
  110.         for (i = 0; i < BLKSIZE; i++)
  111.         {
  112.             this->buf.c[i] = 0;
  113.         }
  114.     }
  115.  
  116.     /* This isn't really a change, but it does potentially invalidate
  117.      * the kinds of shortcuts that the "changes" variable is supposed
  118.      * to protect us from... so count it as a change.
  119.      */
  120.     changes++;
  121.  
  122.     /* mark it as being "not dirty" */
  123.     this->dirty = 0;
  124.  
  125.     /* return it */
  126.     newtoo = toonew;
  127.     toonew = this;
  128.     return &this->buf;
  129. }
  130.  
  131.  
  132.  
  133. /* This function writes a block out to the temporary file */
  134. void blkflush(this)
  135.     REG struct _blkbuf    *this;    /* the buffer to flush */
  136. {
  137.     long        seekpos;    /* seek position of the new block */
  138.     unsigned short    physical;    /* physical block number */
  139.  
  140.     /* if its empty (an orphan blkadd() maybe?) then make it dirty */
  141.     if (this->logical && !*this->buf.c)
  142.     {
  143.         blkdirty(&this->buf);
  144.     }
  145.  
  146.     /* if it's an empty buffer or a clean version is on disk, quit */
  147.     if (!this->logical || hdr.n[this->logical] && !this->dirty)
  148.     {
  149.         return;
  150.     }
  151.  
  152.     /* find a free place in the file */
  153. #ifndef NO_RECYCLE
  154.     seekpos = allocate();
  155.     lseek(tmpfd, seekpos, 0);
  156. #else
  157.     seekpos = lseek(tmpfd, 0L, 2);
  158. #endif
  159.     physical = seekpos / BLKSIZE;
  160.  
  161.     /* put the block there */
  162.     if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
  163.     {
  164.         msg("Trouble writing to tmp file");
  165.     }
  166.     this->dirty = FALSE;
  167.  
  168.     /* update the header so it knows we put it there */
  169.     hdr.n[this->logical] = physical;
  170. }
  171.  
  172.  
  173. /* This function sets a block's "dirty" flag or deletes empty blocks */
  174. void blkdirty(bp)
  175.     BLK    *bp;    /* buffer returned by blkget() */
  176. {
  177.     REG int        i, j;
  178.     REG char    *scan;
  179.     REG int        k;
  180.  
  181.     /* find the buffer */
  182.     for (i = 0; i < NBUFS && bp != &blk[i].buf; i++)
  183.     {
  184.     }
  185. #ifdef DEBUG
  186.     if (i >= NBUFS)
  187.     {
  188.         msg("blkdirty() called with unknown buffer at 0x%lx", bp);
  189.         return;
  190.     }
  191.     if (blk[i].logical == 0)
  192.     {
  193.         msg("blkdirty called with freed buffer");
  194.         return;
  195.     }
  196. #endif
  197.  
  198.     /* if this block ends with line# INFINITY, then it must have been
  199.      * allocated unnecessarily during tmpstart().  Forget it.
  200.      */
  201.     if (lnum[blk[i].logical] == INFINITY)
  202.     {
  203. #ifdef DEBUG
  204.         if (blk[i].buf.c[0])
  205.         {
  206.             msg("bkldirty called with non-empty extra BLK");
  207.         }
  208. #endif
  209.         blk[i].logical = 0;
  210.         blk[i].dirty = FALSE;
  211.         return;
  212.     }
  213.  
  214.     /* count lines in this block */
  215.     for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++)
  216.     {
  217.         if (*scan == '\n')
  218.         {
  219.             j++;
  220.         }
  221.     }
  222.  
  223.     /* adjust lnum, if necessary */
  224.     k = blk[i].logical;
  225.     j += (lnum[k - 1] - lnum[k]);
  226.     if (j != 0)
  227.     {
  228.         nlines += j;
  229.         while (k < MAXBLKS && lnum[k] != INFINITY)
  230.         {
  231.             lnum[k++] += j;
  232.         }
  233.     }
  234.  
  235.     /* if it still has text, mark it as dirty */
  236.     if (*bp->c)
  237.     {
  238.         blk[i].dirty = TRUE;
  239.     }
  240.     else /* empty block, so delete it */
  241.     {
  242.         /* adjust the cache */
  243.         k = blk[i].logical;
  244.         for (j = 0; j < NBUFS; j++)
  245.         {
  246.             if (blk[j].logical >= k)
  247.             {
  248.                 blk[j].logical--;
  249.             }
  250.         }
  251.  
  252.         /* delete it from hdr.n[] and lnum[] */
  253.         blk[i].logical = 0;
  254.         blk[i].dirty = FALSE;
  255.         while (k < MAXBLKS - 1)
  256.         {
  257.             hdr.n[k] = hdr.n[k + 1];
  258.             lnum[k] = lnum[k + 1];
  259.             k++;
  260.         }
  261.         hdr.n[MAXBLKS - 1] = 0;
  262.         lnum[MAXBLKS - 1] = INFINITY;
  263.     }
  264. }
  265.  
  266.  
  267. /* insert a new block into hdr, and adjust the cache */
  268. BLK *blkadd(logical)
  269.     int    logical;    /* where to insert the new block */
  270. {
  271.     REG int    i;
  272.  
  273.     /* adjust hdr and lnum[] */
  274.     for (i = MAXBLKS - 1; i > logical; i--)
  275.     {
  276.         hdr.n[i] = hdr.n[i - 1];
  277.         lnum[i] = lnum[i - 1];
  278.     }
  279.     hdr.n[logical] = 0;
  280.     lnum[logical] = lnum[logical - 1];
  281.  
  282.     /* adjust the cache */
  283.     for (i = 0; i < NBUFS; i++)
  284.     {
  285.         if (blk[i].logical >= logical)
  286.         {
  287.             blk[i].logical++;
  288.         }
  289.     }
  290.  
  291.     /* return the new block, via blkget() */
  292.     return blkget(logical);
  293. }
  294.  
  295.  
  296. /* This function forces all dirty blocks out to disk */
  297. void blksync()
  298. {
  299.     int    i;
  300.  
  301.     for (i = 0; i < NBUFS; i++)
  302.     {
  303.         /* blk[i].dirty = TRUE; */
  304.         blkflush(&blk[i]);
  305.     }
  306.     if (*o_sync)
  307.     {
  308.         sync();
  309.     }
  310. }
  311.  
  312. /*------------------------------------------------------------------------*/
  313.  
  314. static MARK    undocurs;    /* where the cursor should go if undone */
  315. static long    oldnlines;
  316. static long    oldlnum[MAXBLKS];
  317.  
  318.  
  319. /* This function should be called before each command that changes the text.
  320.  * It defines the state that undo() will reset the file to.
  321.  */
  322. void beforedo(forundo)
  323.     int        forundo;    /* boolean: is this for an undo? */
  324. {
  325.     REG int        i;
  326.     REG long    l;
  327.  
  328.     /* if this is a nested call to beforedo, quit! Use larger context */
  329.     if (b4cnt++ > 0)
  330.     {
  331.         return;
  332.     }
  333.  
  334.     /* force all block buffers to disk */
  335.     blksync();
  336.  
  337. #ifndef NO_RECYCLE
  338.     /* perform garbage collection on blocks from tmp file */
  339.     garbage();
  340. #endif
  341.  
  342.     /* force the header out to disk */
  343.     lseek(tmpfd, 0L, 0);
  344.     if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE)
  345.     {
  346.         msg("Trouble writing header to tmp file ");
  347.     }
  348.  
  349.     /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
  350.     if (forundo)
  351.     {
  352.         for (i = 0; i < MAXBLKS; i++)
  353.         {
  354.             l = lnum[i];
  355.             lnum[i] = oldlnum[i];
  356.             oldlnum[i] = l;
  357.         }
  358.         l = nlines;
  359.         nlines = oldnlines;
  360.         oldnlines = l;
  361.     }
  362.     else
  363.     {
  364.         for (i = 0; i < MAXBLKS; i++)
  365.         {
  366.             oldlnum[i] = lnum[i];
  367.         }
  368.         oldnlines = nlines;
  369.     }
  370.  
  371.     /* save the cursor position */
  372.     undocurs = cursor;
  373.  
  374.     /* upon return, the calling function continues and makes changes... */
  375. }
  376.  
  377. /* This function marks the end of a (nested?) change to the file */
  378. void afterdo()
  379. {
  380.     if (--b4cnt)
  381.     {
  382.         /* after abortdo(), b4cnt may decribe nested beforedo/afterdo
  383.          * pairs incorrectly.  If it is decremented to often, then
  384.          * keep b4cnt sane but don't do anything else.
  385.          */
  386.         if (b4cnt < 0)
  387.             b4cnt = 0;
  388.  
  389.         return;
  390.     }
  391.  
  392.     /* make sure the cursor wasn't left stranded in deleted text */
  393.     if (markline(cursor) > nlines)
  394.     {
  395.         cursor = MARK_LAST;
  396.     }
  397.     /* NOTE: it is still possible that markidx(cursor) is after the
  398.      * end of a line, so the Vi mode will have to take care of that
  399.      * itself */
  400.  
  401.     /* if a significant change has been made to this file, then set the
  402.      * MODIFIED flag.
  403.      */
  404.     if (significant)
  405.     {
  406.         setflag(file, MODIFIED);
  407.         setflag(file, UNDOABLE);
  408.     }    
  409. }
  410.  
  411. /* This function cuts short the current set of changes.  It is called after
  412.  * a SIGINT.
  413.  */
  414. void abortdo()
  415. {
  416.     /* finish the operation immediately. */
  417.     if (b4cnt > 0)
  418.     {
  419.         b4cnt = 1;
  420.         afterdo();
  421.     }
  422.  
  423.     /* in visual mode, the screen is probably screwed up */
  424.     if (mode == MODE_COLON)
  425.     {
  426.         mode = MODE_VI;
  427.     }
  428.     if (mode == MODE_VI)
  429.     {
  430.         redraw(MARK_UNSET, FALSE);
  431.     }
  432. }
  433.  
  434. /* This function discards all changes made since the last call to beforedo() */
  435. int undo()
  436. {
  437.     BLK        oldhdr;
  438.  
  439.     /* if beforedo() has never been run, fail */
  440.     if (!tstflag(file, UNDOABLE))
  441.     {
  442.         msg("You haven't modified this file yet.");
  443.         return FALSE;
  444.     }
  445.  
  446.     /* read the old header form the tmp file */
  447.     lseek(tmpfd, 0L, 0);
  448.     if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE)
  449.     {
  450.         msg("Trouble rereading the old header from tmp file");
  451.     }
  452.  
  453.     /* "do" the changed version, so we can undo the "undo" */
  454.     cursor = undocurs;
  455.     beforedo(TRUE);
  456.     afterdo();
  457.  
  458.     /* wipe out the block buffers - we can't assume they're correct */
  459.     blkinit();
  460.  
  461.     /* use the old header -- and therefore the old text blocks */
  462.     hdr = oldhdr;
  463.  
  464.     /* This is a change */
  465.     significant = TRUE;
  466.     changes++;
  467.  
  468.     return TRUE;
  469. }
  470.